package gwt.seca.client.agents;

import static gwt.g3d.client.math.MatrixStack.MODELVIEW;

import javax.vecmath.Matrix3f;
import javax.vecmath.Point3i;
import javax.vecmath.Vector3f;

import gwt.seca.client.IEntity;
import gwt.seca.client.Renderer;
import gwt.seca.client.SceneManager;
import gwt.seca.client.SoundManager;
import gwt.seca.client.util.AxisAlignedBox;
import gwt.seca.client.util.Sphere;

public abstract class AbstractModel implements IEntity {
	
	public static enum Version {};
	
	public AbstractModel(SceneManager sceneMgr, String name) {
		mSceneMgr = sceneMgr;
		mName = name;
		mSoundMgr = null;
		mLocalAABB = new AxisAlignedBox(-.5f, -.5f, -.5f, .5f, .5f, .5f);
		mLocalBoundingSphere = new Sphere(new Vector3f(), .5f);
		mPosition = new Vector3f();
		mRotation = new Vector3f();
		mAnimation = null;
		mCycle = 1;
		mLastStepTime = 0;
		mStepPeriod = 500;
		mDimensions = 2;
		mDiscrete = true;
	}
	abstract public void init();
	abstract public void dispose();
	
	public void update(double elaspsedTime) {
		//TODO abstract this method after the continuous part
		double currTime = System.currentTimeMillis();
		if (currTime-mLastStepTime > mStepPeriod) {
			if (mAnimation != null) 
				mAnimation.stop();
			mLastStepTime = currTime;
			updateAgent(elaspsedTime);
			mCycle++;
			if (mAnimation != null && mAnimation.isStarted())
				mAnimation.start();
		} else {
			if (mAnimation != null) 
				mAnimation.update(currTime-mLastStepTime);
		}
	}
	abstract protected void updateAgent(double elaspsedTime);
	
	public void render(Renderer renderer) {
		//Order of transformations in OpenGL
		MODELVIEW.push();
		////Move entity to its position
		MODELVIEW.translate(mPosition.x, mPosition.y, mPosition.z);
		////Rotate entity around its origin
		MODELVIEW.rotate((float) Math.toRadians(mRotation.z), 0, 0, 1); //+Pi/2 so the agent face Y when Theta == 0
		////Draw entity
		drawAgent(renderer);
		MODELVIEW.pop();
	}
	abstract protected void drawAgent(Renderer renderer);
	
	public void setSoundManager(SoundManager soundMgr) {
		mSoundMgr = soundMgr;
	}
	public SoundManager getSoundManager() {
		return mSoundMgr;
	}
	
	public AxisAlignedBox getAABB() {
		AxisAlignedBox AABB = new AxisAlignedBox(mLocalAABB);
		AABB.translate(mPosition);
		return AABB;
	}
	public AxisAlignedBox getLocalAABB() {
		return mLocalAABB;
	}
	
	abstract protected boolean turn(float angle);
	abstract protected boolean goForward(float distance);
	
	public void setPosition(Vector3f pos) {
		mPosition.set(pos);
	}
	public void setPosition(Point3i pos) {
		mPosition.setX(pos.getX());
		mPosition.setY(pos.getY());
		mPosition.setZ(pos.getZ());
	}
	public Vector3f getPosition() {
		return new Vector3f(mPosition);
	}
	public void setRotation(Vector3f rot) {
		mRotation.set(rot);
	}
	public Vector3f getRotation() {
		return new Vector3f(mRotation);
	}
	public double getStepPeriod() {
		return mStepPeriod;
	}
	
	public Vector3f localToParentRef(Vector3f localVec) {
		Matrix3f rot = new Matrix3f();
		if (mDimensions == 2) {
			rot.rotZ((float) Math.toRadians(mRotation.z));
		} else if (mDimensions == 3) {
			if (mRotation.x == 0) {
				rot.rotY((float) Math.toRadians(mRotation.y));
				Matrix3f rotZ = new Matrix3f();
				rotZ.rotZ((float) Math.toRadians(mRotation.z));
				rot.mul(rotZ);
			} else {
				rot.rotX((float) Math.toRadians(mRotation.x));
				Matrix3f rotY = new Matrix3f();
				rotY.rotY((float) Math.toRadians(mRotation.y));
				rot.mul(rotY);
				Matrix3f rotZ = new Matrix3f();
				rotZ.rotZ((float) Math.toRadians(mRotation.z));
				rot.mul(rotZ);
			}
		} else {
			rot.setIdentity();
		}
		Vector3f parentVec = new Vector3f();
		rot.transform(localVec, parentVec);
		parentVec.add(mPosition);
		return parentVec;
	}
	
	public boolean isInCell(int i, int j, int k) 
	{
		if (i == Math.round(mPosition.getX()) && j == Math.round(mPosition.getY()) && k == Math.round(mPosition.getZ()))
			return true;
		else 
			return false;
	}
	
	public String getName()
	{
		return mName;
	}
	
	//Scene related
	protected String mName;
	protected SceneManager mSceneMgr;
	protected SoundManager mSoundMgr;
	//Agent related
	protected Vector3f mPosition;
	protected Vector3f mRotation;
	protected AxisAlignedBox mLocalAABB; //Local Axis Aligned Bounding Box
	protected Sphere mLocalBoundingSphere;
	protected boolean mDiscrete; //Or continuous
	protected int mDimensions;
	//Animation
	protected AbstractAnim mAnimation;
	//Update
	protected int mCycle;
	protected double mLastStepTime;
	protected double mStepPeriod;
	
}
